package com.sleepbot.datetimepicker.time;
/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.fourmob.datetimepicker.ResourceTable;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorProperty;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.TextAlignment;
import ohos.app.Context;
import ohos.global.resource.ResourceManager;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

/**
 * A view to show a series of numbers in a circular pattern.
 */
public class RadialTextsView extends Component implements Component.DrawTask {
    private static final String TAG = "RadialTextsView";
    private static HiLogLabel label = new HiLogLabel(HiLog.LOG_APP, 0x000110, "DateTimePickerRTV");
    AnimatorValue mDisappearAnimator;
    AnimatorValue mReappearAnimator;
    AnimatorProperty mDisappearAnimator1;
    AnimatorProperty mReappearAnimator1;

    private final Paint mPaint = new Paint();

    private boolean mDrawValuesReady;
    private boolean mIsInitialized;

    private String[] mTexts;
    private String[] mInnerTexts;

    private boolean mIs24HourMode;
    private boolean mHasInnerCircle;
    private float mCircleRadiusMultiplier;
    private float mAmPmCircleRadiusMultiplier;
    private float mNumbersRadiusMultiplier;
    private float mInnerNumbersRadiusMultiplier;
    private float mTextSizeMultiplier;
    private float mInnerTextSizeMultiplier;

    private int mXCenter;
    private int mYCenter;
    private float mCircleRadius;
    private boolean mTextGridValuesDirty;
    private float mTextSize;
    private float mInnerTextSize;
    private float[] mTextGridHeights;
    private float[] mTextGridWidths;
    private float[] mInnerTextGridHeights;
    private float[] mInnerTextGridWidths;

    private float mAnimationRadiusMultiplier;
 /*   private float mTransitionMidRadiusMultiplier;
    private float mTransitionEndRadiusMultiplier;*/
    private InvalidateUpdateListener mInvalidateUpdateListener;

    /**
     * 构造器
     * @param context 上下文
     **/
    public RadialTextsView(Context context) {
        super(context);
        mIsInitialized = false;
    }

    public void initialize(ResourceManager res, String[] texts, String[] innerTexts,
                           boolean is24HourMode, boolean disappearsOut) {
        addDrawTask(this::onDraw);
        HiLog.info(label, "init");
        if (mIsInitialized) {
            HiLog.error(label, "This RadialTextsView may only be initialized once.");
            return;
        }
        try {
            int numbersTextColor = res.getElement(ResourceTable.Color_numbers_text_color).getColor();
            mPaint.setColor(new Color(numbersTextColor));
            mPaint.setAntiAlias(true);
            mPaint.setTextAlign(TextAlignment.CENTER);
            String texts1[] = texts;
            mTexts = texts1.clone();
           String inners[] = innerTexts;
            mInnerTexts = inners;

            mIs24HourMode = is24HourMode;
            mHasInnerCircle = (innerTexts != null);
            mIs24HourMode = is24HourMode;
            if (is24HourMode) {
                mCircleRadiusMultiplier = Float.parseFloat(
                        res.getElement(ResourceTable.String_circle_radius_multiplier_24HourMode).getString());
            } else {
                mCircleRadiusMultiplier = Float.parseFloat(
                        res.getElement(ResourceTable.String_circle_radius_multiplier).getString());
                mAmPmCircleRadiusMultiplier = Float.parseFloat(
                        res.getElement(ResourceTable.String_ampm_circle_radius_multiplier).getString());
            }

            // Initialize the widths and heights of the grid, and calculate the values for the numbers.
            mTextGridHeights = new float[7];
            mTextGridWidths = new float[7];
            if (mHasInnerCircle) {
                mNumbersRadiusMultiplier = Float.parseFloat(
                        res.getElement(ResourceTable.String_numbers_radius_multiplier_outer).getString());
                mTextSizeMultiplier = Float.parseFloat(
                        res.getElement(ResourceTable.String_text_size_multiplier_outer).getString());
                mInnerNumbersRadiusMultiplier = Float.parseFloat(
                        res.getElement(ResourceTable.String_numbers_radius_multiplier_inner).getString());
                mInnerTextSizeMultiplier = Float.parseFloat(
                        res.getElement(ResourceTable.String_text_size_multiplier_inner).getString());

                mInnerTextGridHeights = new float[7];
                mInnerTextGridWidths = new float[7];
            } else {
                mNumbersRadiusMultiplier = Float.parseFloat(
                        res.getElement(ResourceTable.String_numbers_radius_multiplier_normal).getString());
                mTextSizeMultiplier = Float.parseFloat(
                        res.getElement(ResourceTable.String_text_size_multiplier_normal).getString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        mAnimationRadiusMultiplier = 1;
        mInvalidateUpdateListener = new InvalidateUpdateListener();

        mTextGridValuesDirty = true;
        mIsInitialized = true;
    }

    /**
     * Using the trigonometric Unit Circle, calculate the positions that the text will need to be
     * drawn at based on the specified circle radius. Place the values in the textGridHeights and
     * textGridWidths parameters.
     * @param numbersRadius
     * @param textGridHeights
     * @param textGridWidths
     * @param textSize
     * @param xCenter
     * @param yCenter
     */
    private void calculateGridSizes(float numbersRadius, float xCenter, float yCenter,
                                    float textSize, float[] textGridHeights, float[] textGridWidths) {
        /*
         * The numbers need to be drawn in a 7x7 grid, representing the points on the Unit Circle.
         */
        float offset1 = numbersRadius;
        // cos(30) = a / r => r * cos(30) = a => r * √3/2 = a
        float offset2 = numbersRadius * ((float) Math.sqrt(3)) / 2f;
        // sin(30) = o / r => r * sin(30) = o => r / 2 = a
        float offset3 = numbersRadius / 2f;
        mPaint.setTextSize((int)textSize);
        // We'll need yTextBase to be slightly lower to account for the text's baseline.
        yCenter -= (mPaint.descent() + mPaint.ascent()) / 2;

        textGridHeights[0] = yCenter - offset1;
        textGridWidths[0] = xCenter - offset1;
        textGridHeights[1] = yCenter - offset2;
        textGridWidths[1] = xCenter - offset2;
        textGridHeights[2] = yCenter - offset3;
        textGridWidths[2] = xCenter - offset3;
        textGridHeights[3] = yCenter;
        textGridWidths[3] = xCenter;
        textGridHeights[4] = yCenter + offset3;
        textGridWidths[4] = xCenter + offset3;
        textGridHeights[5] = yCenter + offset2;
        textGridWidths[5] = xCenter + offset2;
        textGridHeights[6] = yCenter + offset1;
        textGridWidths[6] = xCenter + offset1;
    }

    /**
     * Draw the 12 text values at the positions specified by the textGrid parameters.
     * @param textSize
     * @param textGridWidths
     * @param textGridHeights
     * @param canvas
     *
     */
    private void drawTexts(Canvas canvas, int textSize, String[] texts,
                           float[] textGridWidths, float[] textGridHeights) {
        mPaint.setTextSize(textSize);
        HiLog.info(label,"" + texts[0] + ", " + texts[1] + ", " + texts[2] + ", " + texts[3]
         + ", " + texts[4] + ", " + texts[5] + ", " + texts[6] + ", " + texts[7] + ", " + texts[8]
            + ", " + texts[9] + ", " + texts[10] + ", " + texts[11]);
        canvas.drawText(mPaint, texts[0], textGridWidths[3], textGridHeights[0] );
        canvas.drawText(mPaint, texts[1], textGridWidths[4], textGridHeights[1]);
        canvas.drawText(mPaint, texts[2], textGridWidths[5], textGridHeights[2]);
        canvas.drawText(mPaint, texts[3], textGridWidths[6], textGridHeights[3]);
        canvas.drawText(mPaint, texts[4], textGridWidths[5], textGridHeights[4]);
        canvas.drawText(mPaint, texts[5], textGridWidths[4], textGridHeights[5]);
        canvas.drawText(mPaint, texts[6], textGridWidths[3], textGridHeights[6]);
        canvas.drawText(mPaint, texts[7], textGridWidths[2], textGridHeights[5]);
        canvas.drawText(mPaint, texts[8], textGridWidths[1], textGridHeights[4]);
        canvas.drawText(mPaint, texts[9], textGridWidths[0], textGridHeights[3]);
        canvas.drawText(mPaint, texts[10], textGridWidths[1], textGridHeights[2]);
        canvas.drawText(mPaint, texts[11], textGridWidths[2], textGridHeights[1]);
    }

    /**
     * Render the animations for appearing and disappearing.
     */
    private void renderAnimations() {

        mDisappearAnimator = new AnimatorValue();
        mDisappearAnimator.setValueUpdateListener(mInvalidateUpdateListener);
        mDisappearAnimator1 = new AnimatorProperty();
        mReappearAnimator1 = new AnimatorProperty();
    }

    private int times = 1;

    public int getTimes() {
        return times;
    }

    public void setTimes(int times) {
        this.times = times;
    }

    public AnimatorProperty getDisappearAnimator(Component component, float creaseRatio1, float creaseRatio2 ) {
        HiLog.info(label, "getDisappearAnimator1 for animation." + mIsInitialized
            + "   " + mDrawValuesReady + " " + (mDisappearAnimator == null));
        if (!mIsInitialized || !mDrawValuesReady || mDisappearAnimator1 == null) {
            HiLog.error(label, "RadialTextView was not ready for animation." + mIsInitialized +
            " " + mDrawValuesReady + " " + mDisappearAnimator);
            return null;
        }

        mDisappearAnimator1.alpha(0);
        mDisappearAnimator1.alphaFrom(255);
        mDisappearAnimator1.scaleYFrom(creaseRatio1);
        mDisappearAnimator1.scaleYBy(creaseRatio2);
        mDisappearAnimator1.scaleXFrom(creaseRatio1);
        mDisappearAnimator1.scaleXBy(creaseRatio2);
        mDisappearAnimator1.setTarget(component);
        mDisappearAnimator1.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
                HiLog.info(label,"Animator onStart");
            }

            @Override
            public void onStop(Animator animator) {
                HiLog.info(label,"Animator onStop");
            }

            @Override
            public void onCancel(Animator animator) {
                HiLog.info(label,"Animator onCancel");
            }

            @Override
            public void onEnd(Animator animator) {
                HiLog.info(label,"Animator onEnd KK " + times);

                if (times != 1) {
                    HiLog.info(label,"Animator onEnd KK1 ");
                } else {
                    HiLog.info(label,"Animator onEnd KK2 ");
                    times = 2;
                }
            }

            @Override
            public void onPause(Animator animator) {
                HiLog.info(label,"Animator onPause");
            }

            @Override
            public void onResume(Animator animator) {
                HiLog.info(label,"Animator onResume");
            }
        });
        return mDisappearAnimator1;
    }

    public AnimatorProperty getReappearAnimator(Component v,float creaseRatio1, float creaseRatio2) {
        HiLog.info(label, "getReappearAnimator1 for animation." + mIsInitialized
            + " " + mDrawValuesReady + " " + (mReappearAnimator == null));
        if (!mIsInitialized || !mDrawValuesReady || mReappearAnimator1 == null) {
            HiLog.error(label, "reapp RadialTextView was not ready for animation.");
            return null;
        }
        v.setVisibility(VISIBLE);
        mReappearAnimator1.alphaFrom(0);
        mReappearAnimator1.alpha(255);
        mReappearAnimator1.scaleYFrom(creaseRatio1);
        mReappearAnimator1.scaleYBy(creaseRatio2);
        mReappearAnimator1.scaleXFrom(creaseRatio1);
        mReappearAnimator1.scaleXBy(creaseRatio2);

        mReappearAnimator1.setTarget(v);
        mReappearAnimator1.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
                HiLog.info(label,"Reap Animator onStart");
            }

            @Override
            public void onStop(Animator animator) {
                HiLog.info(label,"Reap Animator onStop");
            }

            @Override
            public void onCancel(Animator animator) {
                HiLog.info(label,"Reap Animator onCancel");
            }

            @Override
            public void onEnd(Animator animator) {
                HiLog.info(label,"Animator Reappear onEnd " + times);
            }

            @Override
            public void onPause(Animator animator) {
                HiLog.info(label,"Reap Animator onPause");
            }

            @Override
            public void onResume(Animator animator) {
                HiLog.info(label,"Reap Animator onResume");
            }
        });
        return mReappearAnimator1;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        int viewWidth = getWidth();
        if (viewWidth == 0 || !mIsInitialized) {
            return;
        }

        if (!mDrawValuesReady) {
            mXCenter = getWidth() / 2;
            mYCenter = getHeight() / 2;
            mCircleRadius = Math.min(mXCenter, mYCenter) * mCircleRadiusMultiplier;
            if (!mIs24HourMode) {
                // We'll need to draw the AM/PM circles, so the main circle will need to have
                // a slightly higher center. To keep the entire view centered vertically, we'll
                // have to push it up by half the radius of the AM/PM circles.
                float amPmCircleRadius = mCircleRadius * mAmPmCircleRadiusMultiplier;
                mYCenter -= amPmCircleRadius / 2;
            }

            mTextSize = mCircleRadius * mTextSizeMultiplier;
            if (mHasInnerCircle) {
                mInnerTextSize = mCircleRadius * mInnerTextSizeMultiplier;
            }

            // Because the text positions will be static, pre-render the animations.
            renderAnimations();

            mTextGridValuesDirty = true;
            mDrawValuesReady = true;
        }

        // Calculate the text positions, but only if they've changed since the last onDraw.
        if (mTextGridValuesDirty) {
            float numbersRadius =
                    mCircleRadius * mNumbersRadiusMultiplier * mAnimationRadiusMultiplier;

            // Calculate the positions for the 12 numbers in the main circle.
            calculateGridSizes(numbersRadius, mXCenter, mYCenter,
                    mTextSize, mTextGridHeights, mTextGridWidths);
            if (mHasInnerCircle) {
                // If we have an inner circle, calculate those positions too.
                float innerNumbersRadius =
                        mCircleRadius * mInnerNumbersRadiusMultiplier * mAnimationRadiusMultiplier;
                calculateGridSizes(innerNumbersRadius, mXCenter, mYCenter,
                        mInnerTextSize, mInnerTextGridHeights, mInnerTextGridWidths);
            }
            mTextGridValuesDirty = false;
        }

        // Draw the texts in the pre-calculated positions.
        drawTexts(canvas, (int)mTextSize, mTexts, mTextGridWidths, mTextGridHeights);
        if (mHasInnerCircle) {
            drawTexts(canvas, (int)mInnerTextSize,  mInnerTexts, mInnerTextGridWidths, mInnerTextGridHeights);
        }
    }

    private class InvalidateUpdateListener implements AnimatorValue.ValueUpdateListener {
        @Override
        public void onUpdate(AnimatorValue animatorValue, float v) {
            RadialTextsView.this.invalidate();
        }
    }
}